// Package vm: 虚拟机包
package vm

import (
	"fmt"
	"z-lang/code"
	"z-lang/compiler"
	"z-lang/object"
)

// StackSize 栈最大深度
const StackSize = 2048

// Null null值常量
var Null = &object.Null{}

// True true 布尔值 常量
var True = &object.Boolean{Value: true}
var False = &object.Boolean{Value: false}

// GlobalSize 全局变量最大数量 - 因为这里全局变量的操作数是 16位宽
const GlobalSize = 65536

// MaxFrames 栈最大深度
const MaxFrames = 1024

// VM 虚拟机结构体
type VM struct {
	constants []object.Object // 常量池

	stack []object.Object // 栈
	sp    int             // 栈指针 -始终指向栈中的下一个空闲槽。栈顶的值是 stack[sp - 1]

	globals []object.Object // 全局变量存储表

	frames   []*Frame // 栈帧
	frameIdx int      // 指向下一个栈帧的存储位置下标
}

// New 根据传入的字节码信息创建一个 VM 对象
func New(bytecode *compiler.Bytecode) *VM {
	mainFunc := &object.CompiledFunction{Instructions: bytecode.Instructions}
	mainClosure := &object.Closure{Fn: mainFunc}
	mainFrame := NewFrame(mainClosure, 0)

	frames := make([]*Frame, MaxFrames)
	frames[0] = mainFrame

	return &VM{
		constants: bytecode.Constants,
		stack:     make([]object.Object, StackSize),
		sp:        0,
		globals:   make([]object.Object, GlobalSize),
		frames:    frames,
		frameIdx:  1,
	}
}

// NewWithGlobalStore 根据传入的字节码信息和全局变量信息创建一个 VM 对象
func NewWithGlobalStore(bytecode *compiler.Bytecode, s []object.Object) *VM {
	vm := New(bytecode)
	vm.globals = s
	return vm
}

// Run 虚拟机核心的run方法. 取指 - 解码 - 执行
func (vm *VM) Run() error {
	var ip int
	var ins code.Instructions
	var op code.Opcode

	for vm.currentFrame().ip < len(vm.currentFrame().Instructions())-1 {
		vm.currentFrame().ip++

		// 取指 - 获取操作码
		ip = vm.currentFrame().ip
		ins = vm.currentFrame().Instructions()
		op = code.Opcode(ins[ip])

		switch op {
		case code.OpConstant: // 常量操作
			// 操作码之后跟着操作数, 所以从 ip + 1 开始取
			constIdx := code.ReadUint16(ins[ip+1:])
			vm.currentFrame().ip += 2
			err := vm.push(vm.constants[constIdx])
			if err != nil {
				return err
			}

		case code.OpMinus: // 取负
			err := vm.executeMinusOperator()
			if err != nil {
				return err
			}

		case code.OpBang: // 取反
			err := vm.executeBangOperator()
			if err != nil {
				return err
			}

		case code.OpNot: // ~(not)运算
			err := vm.executeNotOperator()
			if err != nil {
				return err
			}

		case code.OpAnd, code.OpOr, code.OpXor, code.OpLShift, code.OpRShift, code.OpURShift: // 二进制运算操作
			err := vm.executeBinaryOperation(op)
			if err != nil {
				return err
			}

		case code.OpLogicAnd, code.OpLogicOr: // 逻辑判断
			err := vm.executeLogicalOperation(op)
			if err != nil {
				return err
			}

		case code.OpGreaterThan, code.OpNotEqual, code.OpEqual: // 比较操作
			err := vm.executeComparison(op)
			if err != nil {
				return err
			}

		case code.OpTrue, code.OpFalse: // 布尔类型
			var err error
			if op == code.OpTrue {
				err = vm.push(True)
			} else {
				err = vm.push(False)
			}
			if err != nil {
				return err
			}

		case code.OpAdd, code.OpSub, code.OpMul, code.OpDiv: // 算术运算
			err := vm.executeOperation(op)
			if err != nil {
				return err
			}

		case code.OpJump: // 无条件跳转操作
			pos := int(code.ReadUint16(ins[ip+1:]))
			vm.currentFrame().ip = pos - 1

		case code.OpJumpNotTruthy: // 非true跳转
			pos := int(code.ReadUint16(ins[ip+1:]))
			vm.currentFrame().ip += 2

			condition := vm.pop()
			if !isTruth(condition) {
				vm.currentFrame().ip = pos - 1
			}

		case code.OpNull: // NULL值
			err := vm.push(Null)
			if err != nil {
				return err
			}

		case code.OpSetGlobal: // 存储全局变量
			// 获取变量在全局变量表中的存储位置索引
			globalIdx := code.ReadUint16(ins[ip+1:])
			// 这个索引是一个两字节的，所以这里需要往前推2个字节
			vm.currentFrame().ip += 2
			vm.globals[globalIdx] = vm.pop()

		case code.OpGetGlobal: // 获取全局变量
			// 获取变量在全局变量表中的存储位置索引
			globalIdx := code.ReadUint16(ins[ip+1:])
			// 这个索引是一个两字节的，所以这里需要往前推2个字节
			vm.currentFrame().ip += 2
			err := vm.push(vm.globals[globalIdx])
			if err != nil {
				return err
			}

		case code.OpArray: // 处理数组
			// 先从栈中取出数组长度
			arrLength := int(code.ReadUint16(ins[ip+1:]))
			// 数组长度占据两个字节
			vm.currentFrame().ip += 2
			// 构建数组
			array := vm.buildArray(vm.sp-arrLength, vm.sp)
			vm.sp = vm.sp - arrLength

			err := vm.push(array)
			if err != nil {
				return err
			}

		case code.OpHash: // 处理哈希表
			// 哈希表键和值的个数
			eleLen := int(code.ReadUint16(ins[ip+1:]))
			// 元素长度占据两个字节
			vm.currentFrame().ip += 2
			// 构建哈希表
			hash, err := vm.buildHash(vm.sp-eleLen, vm.sp)
			if err != nil {
				return err
			}
			vm.sp = vm.sp - eleLen

			err = vm.push(hash)
			if err != nil {
				return err
			}

		case code.OpIndex: // 处理索引表达式
			// 索引
			idx := vm.pop()
			// 被索引对象
			obj := vm.pop()

			err := vm.executeIdxExpression(obj, idx)
			if err != nil {
				return err
			}

		case code.OpCall: // 函数调用 - 从栈顶弹出函数，转为 object.CompiledFunction 执行其中的字节码
			// 读取 code.OpCall 的操作数，确定有几个参数，再根据参数数量计算出函数所在位置
			numArgs := code.ReadUint8(ins[ip+1:])
			vm.currentFrame().ip += 1

			err := vm.executeCall(int(numArgs))
			if err != nil {
				return err
			}

		case code.OpReturnValue: // 返回值 - 先从栈顶弹出返回值，然后从帧的栈中弹出刚刚执行的帧，以便虚拟机主循环的下一次迭代继续在调用者上下文中执行
			// 先从栈顶弹出返回值
			returnValue := vm.pop()
			// 然后从帧的栈中弹出刚刚执行的帧，以便虚拟机主循环的下一次迭代继续在调用者上下文中执行
			frame := vm.popFrame()
			vm.sp = frame.basePointer - 1

			err := vm.push(returnValue)
			if err != nil {
				return err
			}

		case code.OpReturn: // 返回 - return null
			// 从帧的栈中弹出刚刚执行的帧，以便虚拟机主循环的下一次迭代继续在调用者上下文中执行
			frame := vm.popFrame()
			vm.sp = frame.basePointer - 1

			err := vm.push(Null)
			if err != nil {
				return err
			}

		case code.OpSetLocal: // 设置局部变量
			// 读取操作数存储的下标索引
			localIdx := code.ReadUint8(ins[ip+1:])
			// 当前帧的指令指针往前推 1 个位置
			vm.currentFrame().ip += 1
			// 获取当前帧
			frame := vm.currentFrame()
			// 使用当前帧的basePointer并加上绑定的操作数的索引下标，计算出它在栈中的存储位置
			// 并将其值从栈中弹出将其保存到存储位置上
			vm.stack[frame.basePointer+int(localIdx)] = vm.pop()

		case code.OpGetLocal: // 获取局部变量
			localIdx := code.ReadUint8(ins[ip+1:])
			vm.currentFrame().ip += 1

			currentFrame := vm.currentFrame()

			err := vm.push(vm.stack[currentFrame.basePointer+int(localIdx)])
			if err != nil {
				return err
			}

		case code.OpGetBuiltin: // 获取内置函数
			builtinIdx := code.ReadUint8(ins[ip+1:])
			vm.currentFrame().ip += 1

			definition := object.Builtings[builtinIdx]
			err := vm.push(definition.Builtin)
			if err != nil {
				return err
			}

		case code.OpClosure: // 执行闭包函数
			constIdx := code.ReadUint16(ins[ip+1:])
			freeVarNum := code.ReadUint8(ins[ip+3:])
			vm.currentFrame().ip += 3

			err := vm.pushClosure(int(constIdx), int(freeVarNum))
			if err != nil {
				return err
			}

		case code.OpGetFree: // 获取自由变量
			freeIdx := code.ReadUint8(ins[ip+1:])
			vm.currentFrame().ip += 1

			currentClosure := vm.currentFrame().cl
			err := vm.push(currentClosure.Free[freeIdx])
			if err != nil {
				return err
			}

		case code.OpCurrentClosure: // 获取当前闭包函数
			cl := vm.currentFrame().cl
			err := vm.push(cl)
			if err != nil {
				return err
			}

		case code.OpPop: // 单纯的弹栈操作
			vm.pop()

		}
	}
	return nil
}

// pushClosure 在常量池中寻找指定的函数，并将该函数转换为 *object.Closure 进行压栈
func (vm *VM) pushClosure(constIdx, freeVarNum int) error {
	constant := vm.constants[constIdx]
	function, ok := constant.(*object.CompiledFunction)
	if !ok {
		return fmt.Errorf("not a function: %+v", constant)
	}

	free := make([]object.Object, freeVarNum)
	for i := 0; i < freeVarNum; i++ {
		free[i] = vm.stack[vm.sp-freeVarNum+i]
	}
	vm.sp = vm.sp - freeVarNum

	closure := &object.Closure{Fn: function, Free: free}

	return vm.push(closure)
}

// executeCall 执行函数调用
func (vm *VM) executeCall(numArgs int) error {
	// 因为参数是直接压在栈上的，所以这里要 vm.sp -1 - numArgs 来重新回到基地址，找到调用的方法
	callee := vm.stack[vm.sp-1-numArgs]
	switch callee := callee.(type) {
	case *object.Closure:
		return vm.callClosure(callee, numArgs)
	case *object.Builtin:
		return vm.callBuiltin(callee, numArgs)
	default:
		return fmt.Errorf("calling non-function and non-built-in")
	}
}

// callBuiltin 内置函数调用
func (vm *VM) callBuiltin(builtin *object.Builtin, numArgs int) error {
	args := vm.stack[vm.sp-numArgs : vm.sp]

	result := builtin.Fn(args...)
	vm.sp = vm.sp - numArgs - 1

	if result != nil {
		_ = vm.push(result)
	} else {
		_ = vm.push(Null)
	}

	return nil
}

// callClosure 方法调用
func (vm *VM) callClosure(cl *object.Closure, numArgs int) error {
	if cl.Fn.NumParameters != numArgs {
		return fmt.Errorf("wrong number of arguments: want=%d, got=%d", cl.Fn.NumParameters, numArgs)
	}
	frame := NewFrame(cl, vm.sp-numArgs)
	vm.pushFrame(frame)
	// 为局部变量预留出空间, 这里并没有像JAVA那样，再在栈帧中搞局部变量表，而是直接在当前栈上预留出局部变量表所需要的空间
	vm.sp = frame.basePointer + cl.Fn.NumLocals
	return nil
}

// executeHashIdxExpression 处理哈希表类型的索引表达式
func (vm *VM) executeHashIdxExpression(obj, idx object.Object) error {
	// 哈希表对象
	hash := obj.(*object.Hash)
	// 哈希表key
	key, ok := idx.(object.Hashable)
	if !ok {
		return fmt.Errorf("unusable as hash key: %s", idx.Type())
	}

	pair, ok := hash.Pairs[key.HashKey()]
	if !ok {
		return vm.push(Null)
	}

	return vm.push(pair.Value)
}

// executeArrayIdxExpression 处理数组类型的索引表达式
func (vm *VM) executeArrayIdxExpression(obj, idx object.Object) error {
	// 索引下标
	idxVal := idx.(*object.Integer).Value
	// 数组
	array := obj.(*object.Array)

	max := int64(len(array.Elements) - 1)

	if idxVal < 0 || idxVal > max {
		return vm.push(Null)
	}

	return vm.push(array.Elements[idxVal])
}

// executeIdxExpression 处理索引表达式
func (vm *VM) executeIdxExpression(obj, idx object.Object) error {
	switch {
	case obj.Type() == object.ArrayObj && idx.Type() == object.IntegerObj:
		return vm.executeArrayIdxExpression(obj, idx)
	case obj.Type() == object.HashObj:
		return vm.executeHashIdxExpression(obj, idx)
	default:
		return fmt.Errorf("index operator not supported: %s", obj.Type())
	}
}

// buildHash 传入起止下标，从虚拟机栈中获取哈希表的键和值数据并构建出一个哈希表
func (vm *VM) buildHash(startIdx, endIdx int) (object.Object, error) {
	hashPairs := make(map[object.HashKey]object.HashPair)

	for i := startIdx; i < endIdx; i += 2 {
		key := vm.stack[i]
		val := vm.stack[i+1]

		pair := object.HashPair{Key: key, Value: val}

		hashKey, ok := key.(object.Hashable)
		if !ok {
			return nil, fmt.Errorf("unusable as hash key: %s", key.Type())
		}
		hashPairs[hashKey.HashKey()] = pair
	}

	return &object.Hash{Pairs: hashPairs}, nil
}

// buildArray 传入起止下标，从虚拟机栈中获取数组的元素并构建出一个数组
func (vm *VM) buildArray(startIdx, endIdx int) object.Object {
	eleArr := make([]object.Object, endIdx-startIdx)
	for i := startIdx; i < endIdx; i++ {
		eleArr[i-startIdx] = vm.stack[i]
	}
	return &object.Array{Elements: eleArr}
}

// executeLogicalOperation 执行 逻辑判断操作
func (vm *VM) executeLogicalOperation(opcode code.Opcode) error {
	right := vm.pop()
	left := vm.pop()

	if left.Type() != object.BooleanObj || right.Type() != object.BooleanObj {
		return fmt.Errorf("unsupported types for %d operation: %s %s", opcode, left.Type(), right.Type())
	}

	switch opcode {
	case code.OpLogicAnd:
		if left == True && right == True {
			return vm.push(True)
		}
		return vm.push(False)
	case code.OpLogicOr:
		if left == True || right == True {
			return vm.push(True)
		}
		return vm.push(False)
	default:
		return vm.push(False)
	}
}

// executeBinaryOperation 执行 二进制运算操作
func (vm *VM) executeBinaryOperation(opcode code.Opcode) error {
	right := vm.pop()
	left := vm.pop()

	if left.Type() != object.IntegerObj || right.Type() != object.IntegerObj {
		return fmt.Errorf("unsupported types for %d operation: %s %s", opcode, left.Type(), right.Type())
	}

	rightVal := right.(*object.Integer).Value
	leftVal := left.(*object.Integer).Value

	switch opcode {
	case code.OpAnd:
		return vm.push(&object.Integer{Value: leftVal & rightVal})
	case code.OpOr:
		return vm.push(&object.Integer{Value: leftVal | rightVal})
	case code.OpXor:
		return vm.push(&object.Integer{Value: leftVal ^ rightVal})
	case code.OpLShift:
		if rightVal < 1 {
			return fmt.Errorf("unsupported operator: %s << %s. << operator right value must be > 0", left.Type(), right.Type())
		}
		return vm.push(&object.Integer{Value: leftVal << rightVal})
	case code.OpRShift:
		if rightVal < 1 {
			return fmt.Errorf("unsupported operator: %s >> %s. >> operator right value must be > 0", left.Type(), right.Type())
		}
		return vm.push(&object.Integer{Value: leftVal >> rightVal})
	case code.OpURShift:
		if rightVal < 1 {
			return fmt.Errorf("unsupported operator: %s >>> %s. >>> operator right value must be > 0", left.Type(), right.Type())
		}
		u := uint64(leftVal)
		return vm.push(&object.Integer{Value: int64(u >> rightVal)})
	default:
		return fmt.Errorf("unknown operator: %d ", opcode)
	}
}

// executeMinusOperator 执行 - 取负操作
func (vm *VM) executeMinusOperator() error {
	operand := vm.pop()
	if operand.Type() != object.IntegerObj {
		return fmt.Errorf("unsupported types for minus operation: %s", operand.Type())
	}
	value := operand.(*object.Integer).Value
	return vm.push(&object.Integer{Value: -value})
}

// executeBangOperator 执行 ! 取反操作
func (vm *VM) executeBangOperator() error {
	operand := vm.pop()
	switch operand {
	case True:
		return vm.push(False)
	case False:
		return vm.push(True)
	case Null:
		return vm.push(True)
	default:
		return vm.push(False)
	}
}

// executeNotOperator 执行 ~ not 运算
func (vm *VM) executeNotOperator() error {
	operand := vm.pop()
	if operand.Type() != object.IntegerObj {
		return fmt.Errorf("unsupported types for binary not operation: %s", operand.Type())
	}
	val := operand.(*object.Integer).Value
	return vm.push(&object.Integer{Value: ^val})
}

// executeIntegerComparison 执行整数比较操作
func (vm *VM) executeIntegerComparison(opcode code.Opcode, left, right object.Object) error {
	leftVal := left.(*object.Integer).Value
	rightVal := right.(*object.Integer).Value

	switch opcode {
	case code.OpEqual:
		return vm.push(nativeBoolToBooleanObj(rightVal == leftVal))
	case code.OpNotEqual:
		return vm.push(nativeBoolToBooleanObj(rightVal != leftVal))
	case code.OpGreaterThan:
		return vm.push(nativeBoolToBooleanObj(leftVal > rightVal))
	default:
		return fmt.Errorf("unknown operator: %d ", opcode)
	}
}

// executeComparison 执行比较操作
func (vm *VM) executeComparison(opcode code.Opcode) error {
	right := vm.pop()
	left := vm.pop()

	if left.Type() == object.IntegerObj && right.Type() == object.IntegerObj {
		return vm.executeIntegerComparison(opcode, left, right)
	}
	switch opcode {
	case code.OpEqual:
		return vm.push(nativeBoolToBooleanObj(right == left))
	case code.OpNotEqual:
		return vm.push(nativeBoolToBooleanObj(right != left))
	default:
		return fmt.Errorf("unknown operator: %d (%s%s)", opcode, left.Type(), right.Type())
	}
}

// executeIntegerOperation 执行算术运算操作
func (vm *VM) executeIntegerOperation(opcode code.Opcode, left, right object.Object) error {
	leftVal := left.(*object.Integer).Value
	rightVal := right.(*object.Integer).Value

	var result int64
	switch opcode {
	case code.OpAdd:
		result = leftVal + rightVal
	case code.OpSub:
		result = leftVal - rightVal
	case code.OpMul:
		result = leftVal * rightVal
	case code.OpDiv:
		result = leftVal / rightVal
	default:
		return fmt.Errorf("unknown integer operator: %d", opcode)
	}

	return vm.push(&object.Integer{Value: result})
}

func (vm *VM) executeStringOperation(opcode code.Opcode, left, right object.Object) error {
	if opcode != code.OpAdd {
		return fmt.Errorf("unknown string operator: %d", opcode)
	}
	leftVal := left.(*object.String).Value
	rightVal := right.(*object.String).Value

	return vm.push(&object.String{Value: leftVal + rightVal})
}

// executeOperation 执行运算操作
func (vm *VM) executeOperation(opcode code.Opcode) error {
	right := vm.pop()
	left := vm.pop()

	rightType := right.Type()
	leftType := left.Type()

	switch {
	case leftType == object.IntegerObj && rightType == object.IntegerObj:
		return vm.executeIntegerOperation(opcode, left, right)
	case leftType == object.StringObj && rightType == object.StringObj:
		return vm.executeStringOperation(opcode, left, right)
	}

	return fmt.Errorf("unsupported types for bianry operation: %s %s", leftType, rightType)
}

// pop 从虚拟机栈中弹栈
func (vm *VM) pop() object.Object {
	obj := vm.stack[vm.sp-1]
	vm.sp--
	return obj
}

// push 将数据压到虚拟机栈栈顶
func (vm *VM) push(obj object.Object) error {
	if vm.sp >= StackSize {
		return fmt.Errorf("stack overflow")
	}
	vm.stack[vm.sp] = obj
	vm.sp++
	return nil
}

// StackTop 获取栈顶的元素
func (vm *VM) StackTop() object.Object {
	if vm.sp == 0 {
		return nil
	}
	return vm.stack[vm.sp-1]
}

// LastPoppedStackElem 弹出虚拟机栈中最后的元素
func (vm *VM) LastPoppedStackElem() object.Object {
	return vm.stack[vm.sp]
}

// currentFrame 获取当前栈帧
func (vm *VM) currentFrame() *Frame {
	return vm.frames[vm.frameIdx-1]
}

// pushFrame 往栈中新增栈帧
func (vm *VM) pushFrame(frame *Frame) {
	vm.frames[vm.frameIdx] = frame
	vm.frameIdx++
}

// popFrame 弹出栈帧
func (vm *VM) popFrame() *Frame {
	vm.frameIdx--
	return vm.frames[vm.frameIdx]
}

// nativeBoolToBooleanObj go语言boolean转z-lang的boolean
func nativeBoolToBooleanObj(b bool) *object.Boolean {
	if b {
		return True
	}
	return False
}

// isTruth 判断是否为真，默认为 真
func isTruth(obj object.Object) bool {
	switch obj := obj.(type) {
	case *object.Boolean:
		return obj.Value
	case *object.Null:
		return false
	default:
		return true
	}
}
